package com.ruoyi.contract.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.annotation.CompanyScope;

import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;

import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.contract.annotation.enums.ConBusinessOperType;
import com.ruoyi.contract.constant.ConConstants;
import com.ruoyi.contract.domain.*;
import com.ruoyi.contract.domain.vo.ConBusinessDetail;
import com.ruoyi.contract.domain.vo.CusVo;
import com.ruoyi.contract.mapper.*;
import com.ruoyi.contract.service.*;
import com.ruoyi.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 合同主Service业务层处理
 *
 * @author ruoyi
 * @date 2021-09-08
 */
@Service
@PropertySource("classpath:application.yml")
public class ConBusinessServiceImpl extends ServiceImpl<ConBusinessMapper, ConBusiness> implements IConBusinessService {
    @Autowired
    private ConBusinessMapper conBusinessMapper;

    @Autowired
    private IConObjOutService conObjOutService;

    @Autowired
    private IConOperLogService conOperLogService;

    @Autowired
    private IConBusinessTypeService conBusinessTypeService;

    @Autowired
    ISysUserService iSysUserService;

    /**
     * 查询合同主
     *
     * @param conId 合同主ID
     * @return 合同主
     */
    @Override
    public ConBusinessDetail selectConBusinessById(Long conId) {
        return conBusinessMapper.selectConBusinessById(conId);
    }

    /**
     * 查询合同主列表
     *
     * @param conBusiness 合同主
     * @return 合同主
     */
    @CompanyScope
    @Override
    public List<ConBusiness> selectConBusinessList(ConBusiness conBusiness) {
        return conBusinessMapper.selectConBusinessList(conBusiness);
    }

//    /**
//     * 查询合同主列表(多选)
//     *
//     * @param conBusiness 合同主
//     * @return 合同主
//     */
//    @Override
//    @CompanyScope()
//    public List<ConBusiness> selectConBusinessListType(ConBusiness conBusiness, String[] conEffTypeSelect) {
//        return conBusinessMapper.selectConBusinessListType(conBusiness, conEffTypeSelect);
//    }

    /**
     * 新增合同主
     *
     * @param conBusiness 合同主
     * @return 结果
     */
    @Override
    public int insertConBusiness(ConBusiness conBusiness) {
        return conBusinessMapper.insertConBusiness(conBusiness);
    }

    /**
     * 修改合同主
     *
     * @param conBusiness 合同主
     * @return 结果
     */
    @Override
    public int updateConBusiness(ConBusiness conBusiness) {
        return conBusinessMapper.updateConBusiness(conBusiness);
    }

    /**
     * 批量删除合同主
     *
     * @param conIds 需要删除的合同主ID
     * @return 结果
     */
    @Override
    public int deleteConBusinessByIds(String[] conIds) {
        return conBusinessMapper.deleteConBusinessByIds(conIds);
    }

    /**
     * 删除合同主信息
     *
     * @param conId 合同主ID
     * @return 结果
     */
    @Override
    public int deleteConBusinessById(String conId) {
        return conBusinessMapper.deleteConBusinessById(conId);
    }

    @Override
    public List<ConBusiness> listConByWorkInfo(Long deptId, Long cusId, String workType, String supplyType, Date workDate) {
        return conBusinessMapper.listConByWorkInfo(deptId, cusId, workType, supplyType, workDate);
    }

//    @Override
//    public List<ConBusiness> listConByWorkInfo(Long deptId, Long cusId, String workType, String supplyType, Date workDate, String bindOrderId) {
//        return conBusinessMapper.listConByWorkInfoto(deptId, cusId, workType, supplyType, workDate, bindOrderId);
//    }

    /**
     * 是否绑定合同
     * @param conId
     * @param
     * @return
     */
//    @Override
//    public boolean opBindCon(Long conId, String bindType) {
//        ConBusiness conBusiness = this.getById(conId);
//        if (conBusiness == null) {
//            return false;
//        }
//        conBusiness.setIsBind(bindType);
//        return this.updateById(conBusiness);
//    }


    @Override
    public boolean operateAble(Long conId) {
        ConBusiness conBusiness = this.getById(conId);
        if (ConConstants.isTrue.equals(conBusiness.getIsFrozen())) {
            return false;
        } else {
            return true;
        }
    }
    
    
    
    @Autowired
    private ConRuleMapper conRuleMapper;
    
    @Autowired
    private IceBaseMapper iceBaseMapper;
    
    @Autowired
    private IceConfMapper iceConfMapper;
    
    @Autowired
    private IceConfUpdateMapper iceConfUpdateMapper;
    
    @Value("${other.ice.url}")
    private String iceServerUrl;
    
    final private String exportPath = "/ice-server/base/export";

    final private String importPath = "/ice-server/base/import";

    /**
     * 根据iceId，调用ice引擎服务器的接口，获取单条规则的json信息
     * @param iceId ice_base表的主键
     * @return ice引擎服务器返回的json信息
     */
    private JSONObject iceConfExport(Long iceId) {

        // 创建RestTemplate对象
        RestTemplate restTemplate = new RestTemplate();

        // 发送GET请求并等待返回结果
        String url = iceServerUrl + exportPath + "?iceId=" + iceId;
        ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
        String responseBody = response.getBody();

        // 输出返回结果
        // System.out.println("Response: " + responseBody);
        log.debug("Response: " + responseBody);

        // 解析返回结果
        JSONObject responseJsonObject = JSON.parseObject(responseBody);
        if (responseJsonObject == null) {
            return null;
        }
        
        // 判断返回结果是否正确
        String ret = responseJsonObject.getString("ret");
        if (!StringUtils.equals(ret, "0")) {
            return null;
        }
        
        // 如果返回结果正确，则获取data字段（规则导出的json信息）
        JSONObject data = responseJsonObject.getJSONObject("data");
        if (data == null) {
            return null;
        }
        
        return data;
    }

    /**
     * 调用导入接口，导入新的json信息到ice服务器
     * @param data 规则json对象
     * @return ice服务器返回的结果
     */
    private String iceConfImport(JSONObject data) {

        // 创建RestTemplate对象
        RestTemplate restTemplate = new RestTemplate();

        // 以下发送POST请求并等待返回结果

        // 设置请求头为JSON格式
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        // 设置请求体数据
        JSONObject jsonBody = new JSONObject();
        jsonBody.put("json", data.toJSONString());

        // System.out.println("Request: " + jsonBody.toJSONString());
        log.debug("Request: " + jsonBody.toJSONString());

        // 创建HttpEntity对象，将请求头和请求体数据组合起来
        HttpEntity<String> requestEntity = new HttpEntity<>(jsonBody.toJSONString(), headers);

        // 发送POST请求
        String url = iceServerUrl + importPath;
        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);

        // 获取响应结果
        String responseBody = response.getBody();

        // 输出返回结果
        // System.out.println("Response: " + responseBody);
        log.debug("Response: " + responseBody);

        return responseBody;

    }

    /**
     * 针对confIds和confUpdateConfIds，合并id列表并去重
     * @param confIds 规则id列表
     * @param confUpdateConfIds 规则更新id列表
     * @return 合并去重后的id列表
     */
    private List<String> getDistinctConfIds(List<String> confIds, List<String> confUpdateConfIds ) {
        if (confIds == null) {
            return new ArrayList<>(confUpdateConfIds);
        }
        if (confUpdateConfIds == null) {
            return new ArrayList<>(confIds);
        }
        
        // 合并confIds和confUpdateConfIds
        List<String> allConfIds = new ArrayList<>();
        allConfIds.addAll(confIds);
        allConfIds.addAll(confUpdateConfIds);
        
        // 去重
        allConfIds = allConfIds.stream().distinct().collect(Collectors.toList());
        return allConfIds;
    }

    /**
     * 从给定的confs中，根据idKey提取id列表
     * @param confs 规则列表
     * @param idKey id字段名，可能为id或confId
     * @return id列表
     */
    private List<String> extractIds(JSONArray confs, String idKey) {
        if (confs == null || confs.size() == 0) {
            return null;
        }
        
        // 遍历confs，提取id
        List<String> ids = new ArrayList<>();
        for (int i = 0; i < confs.size(); i++) {
            JSONObject conf = confs.getJSONObject(i);
            if (conf == null) {
                continue;
            }
            
            String id = conf.getString(idKey);
            if (!StringUtils.isBlank(id)) {
                ids.add(id);
            }
            
        }
        
        return ids;
    }

    /**
     * 根据给定的id列表，设置新id
     * @param ids id列表
     * @param startNewId 新id的起始值
     * @return
     */
    private Map<String, String> setNewIds(List<String> ids, long startNewId) {
        if (ids == null || ids.size() == 0) {
            return null;
        }
        
        // 遍历ids，设置新id
        Map<String, String> idMap = new HashMap<>();
        for (int i = 0; i < ids.size(); i++) {
            String id = ids.get(i);
            if (StringUtils.isBlank(id)) {
                continue;
            }
            
            String newId = String.valueOf(startNewId + i);
            idMap.put(id, newId);
        }
        
        return idMap;
    }

    /**
     * 根据给定的idMap，替换sonIds中的id
     * @param sonIdsStr sonIds字符串 
     * @param idMap id映射表
     * @return 替换后的sonIds字符串
     */
    private String replaceSonIds(String sonIdsStr, Map<String, String> idMap) {
        
        // 定义分隔符，此处为英文逗号
        final String COMMA_SEPARATOR = ",";

        if (StringUtils.isBlank(sonIdsStr)) {
            return null;
        }
        
        // 遍历sonIds，设置新id
        List<String> sonIds = Arrays.asList(sonIdsStr.split(COMMA_SEPARATOR));
        for (int i = 0; i < sonIds.size(); i++) {
            String sonId = sonIds.get(i);
            // 根据idMap，替换sonId
            if (!StringUtils.isBlank(sonId)) {
                sonIds.set(i, idMap.get(sonId));
            }
        }
        
        // 拼接sonIds，返回
        return StringUtils.join(sonIds, COMMA_SEPARATOR);
    }

    /**
     * 替换base中的相关字段
     * @param base 基础规则
     * @param newIceBaseId 新的ice_base表id
     * @param confIdMap 规则id映射表
     * @return 替换后的base
     */
    private JSONObject replaceBase(JSONObject base, Long newIceBaseId, Map<String, String> confIdMap) {
        if (base == null) {
            return null;
        }
        base.put("id", newIceBaseId);
        base.put("confId", replaceSonIds(base.getString("confId"), confIdMap));
        base.put("name", base.getString("name") + "_" + newIceBaseId);
        
        return base;
    }

    /**
     * 替换confs中的相关字段
     * @param confs ice规则的节点列表
     * @param confIdMap 规则id映射表 
     * @return 替换后的confs
     */
    private JSONArray replaceConfs(JSONArray confs, Map<String, String> confIdMap) {
        if (confs == null || confs.size() == 0) {
            return null;
        }
        // 遍历confs，设置新id
        for (int i = 0; i < confs.size(); i++) {
            JSONObject conf = confs.getJSONObject(i);
            if (conf == null) {
                continue;
            }
            conf.put("id", confIdMap.get(conf.getString("id")));
            conf.put("sonIds", replaceSonIds(conf.getString("sonIds"), confIdMap));
        }
        
        return confs;
    }

    /**
     * 替换confUpdates中的相关字段
     * @param confUpdates ice规则的更新列表
     * @param newIceBaseId 新的ice_base表id
     * @param confUpdateIdMap 规则更新id映射表
     * @param confIdMap  规则id映射表
     * @return 替换后的confUpdates
     */
    private JSONArray replaceConfUpdates(JSONArray confUpdates, Long newIceBaseId, Map<String, String> confUpdateIdMap, Map<String, String> confIdMap) {
        if (confUpdates == null || confUpdates.size() == 0) {
            return null;
        }
        // 遍历confUpdates，设置新id
        for (int i = 0; i < confUpdates.size(); i++) {
            JSONObject confUpdate = confUpdates.getJSONObject(i);
            if (confUpdate == null) {
                continue;
            }
            confUpdate.put("id", confUpdateIdMap.get(confUpdate.getString("id")));
            confUpdate.put("sonIds", replaceSonIds(confUpdate.getString("sonIds"), confIdMap));
            confUpdate.put("iceId", newIceBaseId);
            confUpdate.put("confId", confIdMap.get(confUpdate.getString("confId")));
        }
        
        return confUpdates;
    }

    /**
     * 转换规则导出的json信息，将id替换为新id
     * @param data 规则导出的json信息
     * @param newIceBaseId 新的iceId
     * @return
     */
    private JSONObject transfer(JSONObject data, Long newIceBaseId) {
        
        // 获取base、confs、confUpdates
        JSONObject base = data.getJSONObject("base");
        JSONArray confs = data.getJSONArray("confs");
        JSONArray confUpdates = data.getJSONArray("confUpdates");

        // 获取confIds、confUpdateConfIds
        List<String> confIds = extractIds(confs, "id");
        List<String> confUpdateConfIds = extractIds(confUpdates, "confId");
        
        // 根据confIds、confUpdateConfIds，获取”旧id“和”新id“的映射关系
        Map<String, String> confIdMap = setNewIds(getDistinctConfIds(confIds, confUpdateConfIds), iceConfMapper.selectMaxId() + 1);
        Map<String, String> confUpdateIdMap = setNewIds(extractIds(confUpdates, "id"), iceConfUpdateMapper.selectMaxId() + 1);

        // 遍历base、confs、confUpdates，替换相关的字段
        if (base != null) {
            data.put("base", replaceBase(base, newIceBaseId, confIdMap));
        }
        if (confs != null) {
            data.put("confs", replaceConfs(confs, confIdMap));
        }
        if (confUpdates != null) {
            data.put("confUpdates", replaceConfUpdates(confUpdates, newIceBaseId, confUpdateIdMap, confIdMap));
        }
        
        return data;
        
    }


    /**
     * 复制ice引擎规则
     * @param oldIceBaseId ice_base表的规则id
     * @return 新的ice_base表的规则id
     */
    private Long copyRuleByIceBaseId(Long oldIceBaseId) {

        // 获取新的iceBaseId
        Long newIceBaseId = iceBaseMapper.selectMaxId() + 1;

        // 根据iceConfId，调用导出接口获取规则的json信息
        JSONObject data = iceConfExport(oldIceBaseId);
        if (data == null) {
            return (long) -1;
        }

        // 分析json信息，构造复制后的规则，并调用导入接口，导入新的json信息到ice服务器
        iceConfImport(transfer(data, newIceBaseId));

        return newIceBaseId;
    }

    /**
     * 复制合同条目
     * @param conId 原始合同id
     * @return 新的合同id
     */
    private Long copyConBusinessByConId(Long conId) {

        ConBusiness conBusiness = this.getById(conId);
        if (Objects.isNull(conBusiness)) {
            throw new ServiceException("合同不存在，传入的合同id为：" + conId);
        }

        // 复制合同条目
        SysUser logonUser = iSysUserService.selectUserByUserName(SecurityUtils.getUsername());
        SysUser sysUser = iSysUserService.getOne(
                new QueryWrapper<SysUser>().lambda().eq(SysUser::getUserName, SecurityUtils.getUsername())
        );
        ConBusiness newConBusiness = conBusiness.deepCopyConBusiness();
        newConBusiness.setConId(null);
        // 生成复制后的合同编号，格式为：原合同编号-时间戳
        newConBusiness.setConCode(conBusiness.getConCode() + "-" + System.currentTimeMillis());
        newConBusiness.setHandleName(logonUser.getUserName());
        newConBusiness.setCreateBy(logonUser.getUserName());
        newConBusiness.setCreateTime(new Date());
        newConBusiness.setIsFrozen("0");
        newConBusiness.setIsTemplate("0");
        newConBusiness.setUpdateBy(null);
        newConBusiness.setUpdateTime(null);
        newConBusiness.setRemark(conBusiness.getRemark());
        newConBusiness.setCompanyCode(sysUser.getCompanyCod());

        if (!this.save(newConBusiness)) {
            throw new ServiceException("新复制合同条目插入失败，传入的合同id为：" + conId);
        }
        
        return newConBusiness.getConId();
    }

    /**
     * 复制合同外部对象
     * @param oldConId 原始合同id
     * @param newConId 新合同id
     */
    private void copyConObjOutByConId(Long oldConId, Long newConId) {
        
        if (Objects.isNull(oldConId) || Objects.isNull(newConId)) {
            throw new ServiceException("合同id不能为空，传入的原始合同id为：" + oldConId + "，新合同id为：" + newConId);
        }

        // 复制合同外部对象
        List<ConObjOut> conObjOuts = conObjOutService.list(
                new QueryWrapper<ConObjOut>().lambda().eq(ConObjOut::getConId, oldConId)
        );
        
        for (int i = 0; i < conObjOuts.size(); i++) {
            ConObjOut conObjOut = new ConObjOut();
            conObjOut.setConId(newConId);
            conObjOut.setCustomerGkey(conObjOuts.get(i).getCustomerGkey());
            
            // System.out.println("conObjOut = " + conObjOut);
            if (!conObjOutService.save(conObjOut)) {
                throw new ServiceException("新复制合同外部对象插入失败，传入的原始合同id为：" + oldConId + "，新合同id为：" + newConId + "，合同外部对象为：" + conObjOut);
            }
        }
        
    }

    /**
     * 复制合同业务绑定类型
     * @param oldConId 原始合同id
     * @param newConId 新合同id
     */
    private void copyConBusinessTypeByConId(Long oldConId, Long newConId) {

        if (Objects.isNull(oldConId) || Objects.isNull(newConId)) {
            throw new ServiceException("合同id不能为空，传入的原始合同id为：" + oldConId + "，新合同id为：" + newConId);
        }

        // 复制合同业务绑定类型
        List<ConBusinessType> conBusinessTypes = conBusinessTypeService.list(
                new QueryWrapper<ConBusinessType>().lambda().eq(ConBusinessType::getConId, oldConId)
        );
        
        for (int i = 0; i < conBusinessTypes.size(); i++) {
            conBusinessTypes.get(i).setConId(newConId);
            
            // System.out.println("conBusinessTypes.get(i) = " + conBusinessTypes.get(i));
        }
        
        if (!conBusinessTypeService.saveBatch(conBusinessTypes)) {
            throw new ServiceException("新复制合同业务绑定类型插入失败，传入的原始合同id为：" + oldConId + "，新合同id为：" + newConId + "，合同业务绑定类型为：" + conBusinessTypes);
        }
        
    }

    /**
     * 复制合同规则配置
     * @param oldConId 原始合同id
     * @param newConId 新合同id
     */
    private void copyConRuleByConId(Long oldConId, Long newConId) {

        if (Objects.isNull(oldConId) || Objects.isNull(newConId)) {
            throw new ServiceException("合同id不能为空，传入的原始合同id为：" + oldConId + "，新合同id为：" + newConId);
        }

        // 获取原始合同的所有规则
        List<ConRule> conRuleList = conRuleMapper.selectList(
                new QueryWrapper<ConRule>().lambda().eq(ConRule::getConId, oldConId)
        );

        // 针对每一条规则进行复制
        for (ConRule conRule : conRuleList) {

            // 复制并插入CON_RULE表
            SysUser sysUser = iSysUserService.selectUserByUserName(SecurityUtils.getUsername());
            ConRule newConRule = conRule.deepCopyConRule();
            newConRule.setConId(newConId);
            newConRule.setIceId(conRule.getIceId());
            newConRule.setCreateBy(sysUser.getUserName());
            newConRule.setCreateTime(new Date());
            newConRule.setUpdateBy(null);
            newConRule.setUpdateTime(null);

            // System.out.println("newConRule = " + newConRule);

            if (conRuleMapper.insert(newConRule) != 1) {
                throw new ServiceException("新复制合同规则配置插入失败，传入的原始合同id为：" + oldConId + "，新合同id为：" + newConId + "，合同规则配置为：" + newConRule);
            }
        }

    }

    /**
     * 根据原始合同id，复制合同，主要复制合同条目、合同外部对象、合同业务绑定类型、合同规则配置
     * @param conId 原始合同id
     * @return 新的合同id
     */
    @Transactional
    public Long doConCopy(Long conId) {
        
        if (Objects.isNull(conId)) {
            throw new ServiceException("要复制合同，传入的合同id不能为空");
        }

        Long newConId = copyConBusinessByConId(conId);

        copyConObjOutByConId(conId, newConId);

        copyConBusinessTypeByConId(conId, newConId);

        copyConRuleByConId(conId, newConId);
        
        return newConId;
    }

    /**
     * 复制原始合同中的规则，主要在ice服务器中复制
     * @param oldConId 原始合同id
     * @param newConId 新的合同id
     */
    private void doIceBaseCopy(Long oldConId, Long newConId) {

        if (Objects.isNull(oldConId) || Objects.isNull(newConId)) {
            throw new ServiceException("合同id不能为空，传入的原始合同id为：" + oldConId + "，新合同id为：" + newConId);
        }

        List<ConRule> conRuleList = conRuleMapper.selectList(
                new QueryWrapper<ConRule>().lambda().eq(ConRule::getConId, oldConId)
        );

        // 针对原始合同的每一条规则进行复制
        for (ConRule conRule : conRuleList) {
            // 获取原始合同的ice_id
            Long oldIceBaseId = conRule.getIceId();
            // 复制规则
            Long newIceBaseId = copyRuleByIceBaseId(oldIceBaseId);
            
            // 从数据库中选取con_id为newConId，且ice_id为oldIceBaseId的新复制规则，这是由于在复制合同时，新复制规则的ice_id已经被设为oldIceBaseId
            ConRule newConRule = conRuleMapper.selectOne(
                    new QueryWrapper<ConRule>().lambda()
                            .eq(ConRule::getConId, newConId)
                            .eq(ConRule::getIceId, oldIceBaseId)
            );
            if (Objects.isNull(newConRule)) {
                continue;
            }
            
            // 将新复制的规则的ice_id设为newIceBaseId
            newConRule.setIceId(newIceBaseId);
            
            // 更新新复制的规则（主要是更新ice_id）
            conRuleMapper.updateById(newConRule);
            
        }
        
    }
    

    /**
     * 根据合同id复制出一份新的合同
     * @param conId 合同id
     * @return 新合同
     */
    @Override
    public Long copyContractById(Long conId) {

        if (Objects.isNull(conId)) {
            throw new ServiceException("要复制合同，传入的合同id不能为空");
        }

        Long newConId = doConCopy(conId);

        doIceBaseCopy(conId, newConId);
        
        return newConId;
    }

    @Override
    public String getConCode(String conCode) {
        return conBusinessMapper.getConcode(conCode);
    }

    @Override
	public List<ConBusiness> listContractByProjectId(Long projectId) {
		
        return conBusinessMapper.listContractByProjectId(projectId);
        
	}
	
	
	public List<CusVo> getCusId(Long conId){
        return conBusinessMapper.getCusId(conId);
    }

}
